package ro.msg.quiz.Model;

import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;

import javax.swing.JOptionPane;

import ro.msg.quiz.repository.Question;
import ro.msg.quiz.repository.Quiz;
import ro.msg.quiz.repository.RepositoryQuestion;
import ro.msg.quiz.repository.RepositoryQuiz;

/**
 * Author: Vlad,Roxana This class is the model of quiz.
 */
public class QuizModel implements IQuizModel {
	private RepositoryQuestion questionRepository = new RepositoryQuestion();
	private RepositoryQuiz quizRepository = new RepositoryQuiz();
	private Map<Integer, ArrayList<Integer>> quizQuestions = new HashMap<Integer, ArrayList<Integer>>();

	/**
	 * Constructor
	 */
	public QuizModel() {
		this.questionRepository = null;
		this.quizRepository = null;
		this.quizQuestions = null;
	}

	/**
	 * Constructor
	 * 
	 * @param questionRepository
	 * @param quizRepository
	 */
	public QuizModel(RepositoryQuestion questionRepository,
			RepositoryQuiz quizRepository) {
		this.questionRepository = questionRepository;
		this.quizRepository = quizRepository;
		// readFromFileQuizQuestion();
	}

	/**
	 * @return Returns the repository of questions.
	 */
	@Override
	public RepositoryQuestion getQuestionRepo() {
		return questionRepository;
	}

	/**
	 * Sets the repository of questions.
	 * 
	 * @param questionRepo
	 *            The repository of questions to be set.
	 */
	@Override
	public void setQuestionRepo(RepositoryQuestion questionRepo) {
		questionRepository = questionRepo;
	}

	/**
	 * @return Returns the repository of quizzes.
	 */
	@Override
	public RepositoryQuiz getQuizRepo() {
		return quizRepository;
	}

	/**
	 * Sets the repository of quiz.
	 * 
	 * @param quizRepo
	 *            The repository of quiz to be set.
	 */
	@Override
	public void setQuizRepo(RepositoryQuiz quizRepo) {
		quizRepository = quizRepo;
	}

	/**
	 * @return Returns the map of relations between questions and quizzes.
	 */
	@Override
	public Map<Integer, ArrayList<Integer>> getQuizQuestions() {
		return quizQuestions;
	}

	/**
	 * Sets the relation between quiz and question.
	 * 
	 * @param quizQuestion
	 *            The map of relations to be set.
	 */
	@Override
	public void setQuizQuestions(Map<Integer, ArrayList<Integer>> quizQuestions) {
		this.quizQuestions = quizQuestions;
	}

	/**
	 * @return Returns the iterator for questions.
	 */
	@Override
	public Iterator<Question> QuestionIterator() {
		return questionRepository.QuestionIterator();
	}

	/**
	 * @return Returns the iterator for quizzes.
	 */
	@Override
	public Iterator<Quiz> QuizIterator() {
		return quizRepository.QuizIterator();
	}

	/**
	 * This method adds question to repository of questions.
	 * 
	 * @param question
	 *            The question to be appended to repository of questions.
	 */
	@Override
	public void addQuestion(Question question) {

		this.questionRepository.addQuestion(question);
	}

	/**
	 * This method removes question from repository of questions and map of
	 * relations.
	 */
	@Override
	public boolean removeQuestion(int question) {
		// first we verify if the question did not exist in the first place
		if (this.questionRepository.getQuestion(question) == null) {
			try {
				throw new QuestionException("The question does not exist");
			} catch (QuestionException e) {
				JOptionPane.showMessageDialog(null, e.getMessage(),
						"Error - removeQuestion", JOptionPane.WARNING_MESSAGE);
			}
			return false;
		}
		int j;
		// now we parse all the quizzes in the map and their questions and
		// remove any binding which existed
		for (Integer key : quizQuestions.keySet()) {
			for (j = 0; j < this.quizQuestions.get(key).size(); j++) {
				if (this.quizQuestions.get(key).get(j) == question) {
					this.quizQuestions.get(key).remove(j);
				}
			}
		}
		// we remove the question from the question repository so that the
		// question will not exist anymore anywhere
		this.questionRepository.deleteQuestion(question);
		return true;
	}

	/**
	 * This method adds quiz to repository of quizzes.
	 * 
	 * @param quiz
	 *            The quiz to be appended to repository of quizzes.
	 */
	@Override
	public void addQuiz(Quiz quiz) {
		this.quizRepository.addQuiz(quiz);
		if (!QuizQuestionExists(quiz)) {
			// here I add the quiz to the map and an empty array list of
			// questions id's
			ArrayList<Integer> list = new ArrayList<Integer>();
			this.quizQuestions.put(quiz.getId(), list);
		}
	}

	/**
	 * This method removes quiz and all the binding in the quiz-map.
	 * 
	 * @param id
	 *            The id of quiz to be removed.
	 */
	@Override
	public void removeQuiz(int id) {
		this.quizRepository.removeQuizById(id);
		// also we want to remove the quiz from the QuizQuestions
		this.quizQuestions.remove(id);
	}

	/**
	 * This method verifies if the quiz exists in the map.
	 * 
	 * @return Return true if the quiz exists or false if fail.
	 */
	@Override
	public boolean QuizQuestionExists(Quiz quiz) {
		for (Integer key : quizQuestions.keySet())
			if (key == quiz.getId())
				return true;
		return false;

	}

	/**
	 * This method appends questions to quiz.
	 * 
	 * @param quiz
	 *            The id of quiz.
	 * @param question
	 *            The id of question to be appended of quiz.
	 */
	@Override
	public boolean addQuestionToQuiz(Integer quiz, Integer question) {
		if (this.questionRepository.getQuestion(question) == null) {
			try {
				throw new QuestionException("The question does not exist");
			} catch (QuestionException e) {
				JOptionPane.showMessageDialog(null, e.getMessage(),
						"Error - addQuestionToQuiz",
						JOptionPane.WARNING_MESSAGE);
			}
			return false;
		} else if (this.quizQuestions.containsKey(quiz) == false) {
			try {
				throw new QuizException("The quiz does not exist");
			} catch (QuizException e) {
				JOptionPane.showMessageDialog(null, e.getMessage(),
						"Error - addQuestionToQuiz",
						JOptionPane.WARNING_MESSAGE);
			}
			return false;
		} else {
			for (int i = 0; i < this.quizQuestions.get(quiz).size(); i++)
				if (this.quizQuestions.get(quiz).get(i) == question) {
					try {
						throw new QuestionException(
								"This questions is already added to the quiz");
					} catch (QuestionException e) {
						JOptionPane.showMessageDialog(null, e.getMessage(),
								"Error - addQuestionToQuiz",
								JOptionPane.WARNING_MESSAGE);
					}
					return false;
				}
		}
		this.quizQuestions.get(quiz).add(question);
		return true;
	}

	/**
	 * This method removes a question from quiz, not from question repository.
	 * 
	 * @param quiz
	 *            The id of quiz.
	 * @param question
	 *            The id of question to be removed from quiz.
	 */
	@Override
	public boolean removeQuestionFromQuiz(Integer quiz, Integer question) {
		if (this.questionRepository.getQuestion(question) == null) {
			try {
				throw new QuestionException("The question does not exist");
			} catch (QuestionException e) {
				JOptionPane.showMessageDialog(null, e.getMessage(),
						"Error - removeQuestionFromQuiz",
						JOptionPane.WARNING_MESSAGE);
			}
			return false;
		} else if (this.quizQuestions.containsKey(quiz) == false) {
			try {
				throw new QuizException("The quiz does not exist");
			} catch (QuizException e) {
				JOptionPane.showMessageDialog(null, e.getMessage(),
						"Error - removeQuestionFromQuiz",
						JOptionPane.WARNING_MESSAGE);
			}
			return false;
		} else {
			for (int i = 0; i < this.quizQuestions.get(quiz).size(); i++)
				if (this.quizQuestions.get(quiz).get(i) == question) {
					this.quizQuestions.get(quiz).remove(i);
					return true;
				}
		}
		// this.quizQuestions.get(quiz).add(question);
		return false;
	}

	/**
	 * This method prints all the values from relations map.
	 */
	@Override
	public void printMap() {
		Iterator iterator = this.quizQuestions.keySet().iterator();
		ArrayList<Integer> values;
		while (iterator.hasNext()) {
			Integer key = (Integer) iterator.next();
			values = this.quizQuestions.get(key);
			System.out.println(this.quizRepository.getQuiz(key).toString());
			for (Integer temp : values) {
				System.out.println(this.questionRepository.getQuestion(temp)
						.toString());
			}
		}
	}

	/**
	 * This method reads file relations between quiz and questions from
	 * quizQuestions.txt.
	 */
	@Override
	public void readFromFileQuizQuestion(String path) {
		Scanner sc = null;
		try {
			sc = new Scanner(new FileReader(path + "\\quizQuestions.txt"));
			String line = "";
			int idQuiz = 0, idQuest = 0;
			ArrayList<Integer> idQuestList;
			ArrayList<Integer> quizes = new ArrayList<Integer>();
			ArrayList<Integer> quests = new ArrayList<Integer>();
			ArrayList<Integer> smallListQuiz = new ArrayList<Integer>();
			int oldQuiz = -1;

			while (sc.hasNextLine()) {
				line = sc.nextLine();
				String[] attr = line.split("[ ]");
				idQuiz = Integer.parseInt(attr[0]);
				idQuest = Integer.parseInt(attr[1]);
				quizes.add(idQuiz);
				quests.add(idQuest);
				if (idQuiz != oldQuiz) {
					smallListQuiz.add(idQuiz);
				}
				oldQuiz = idQuiz;
			}

			for (int i = 0; i < smallListQuiz.size(); i++) {
				idQuestList = new ArrayList<Integer>();
				for (int j = 0; j < quizes.size(); j++) {
					if (quizes.get(j) == smallListQuiz.get(i)) {
						idQuestList.add(quests.get(j));
					}
				}
				quizQuestions.put(smallListQuiz.get(i), idQuestList);
			}
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, e.toString(),
					"Error - QuizQuestionIn", JOptionPane.WARNING_MESSAGE);
		}
	}

	/**
	 * This method writes the map of relations in quizQuestionsOut.txt.
	 */
	@Override
	public void writeToFileQuizQuestion(String path) {
		BufferedWriter bufferedWriter = null;
		try {
			String extPath = path + "\\quizQuestions.txt";
			bufferedWriter = new BufferedWriter(new FileWriter(extPath));
			Iterator iterator = this.quizQuestions.keySet().iterator();
			ArrayList<Integer> values;
			while (iterator.hasNext()) {
				Integer key = (Integer) iterator.next();
				values = this.quizQuestions.get(key);
				for (Integer temp : values) {
					bufferedWriter.write(this.questionRepository.getQuestion(
							temp).toString());
					bufferedWriter.newLine();
				}
			}
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, e.toString(),
					"Error - QuizQuestionOut", JOptionPane.WARNING_MESSAGE);
		} finally {
			try {
				bufferedWriter.close();
			} catch (IOException e) {
				JOptionPane.showMessageDialog(null, e.toString(),
						"Error - QuizQuestionOut", JOptionPane.WARNING_MESSAGE);
			}
		}
	}

	@Override
	/**
	 * This method calls all the write to file.txt methods.
	 */
	public void ExportTxt(String path) {
		questionRepository.writeToFileQuestions(path);
		quizRepository.writeToFileQuiz(path);
		writeToFileQuizQuestion(path);
	}

	/**
	 * This method returns the number of questions that belong to a specific
	 * quiz
	 * 
	 * @param quiz
	 * @return
	 */
	public Integer getNoOfQuestions(Integer quiz) {
		return this.quizQuestions.get(quiz).size();
	}

	/**
	 * calls the read of quizzes
	 */
	public void readFromFileQuiz(String path) {
		quizRepository.readFromFileQuiz(path);
	}

	/**
	 * calls the write of quizzes
	 */
	public void writeToFileQuiz(String path) {
		quizRepository.writeToFileQuiz(path);
	}

	/**
	 * calls the write of questions
	 * 
	 * @param path
	 */
	public void readFromFileQuestion(String path) {
		questionRepository.readFromFileQuestions(path);
	}

	/**
	 * calls the write of questions
	 * 
	 * @param path
	 */
	public void writeToFileQuestion(String path) {
		questionRepository.writeToFileQuestions(path);
	}
}

class QuizException extends Exception {
	public QuizException(String message) {
		super(message);
	}
}

class QuestionException extends Exception {
	public QuestionException(String message) {
		super(message);
	}
}
